home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / networking / otpapsampleserver / pappostscriptstuff.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  16.8 KB  |  586 lines

  1. /*
  2.     File: PAPPostScriptStuff.c
  3.     
  4.     By Rich Kubota
  5.     
  6.     
  7.     The following are routines used to support the handling of Postscript query 
  8.     processing.  These are my own routines implemented to provide support for
  9.     the postscript queries from a laserwriter client.  All of the postscript
  10.     parsing related functions are included in this file.  I'd like to warn the
  11.     reader that this code is written to handle some basic postscript queries as
  12.     sent by Apple LaserWriter Client prior to v8.5.
  13.     
  14.     The design of the routine presented here are to
  15.     1. recognize postscript queries as opposed to postscript data
  16.     2. parse postscript commands and return the default response.
  17.     
  18.     This code does not support other Postscript clients or handling of other
  19.     than the default responses.  This may not be sufficient to support your
  20.     printer and client.
  21.     
  22.     For more information on processing Postscript queries, contact Adobe for their
  23.     technote on this subject *** Get web address ***
  24.     
  25.     A RIP or printer spooler would have to handle queries in a different manner.
  26.     In order to respond to the queris the spooler code might already have the
  27.     desired response, or may have to delay response until it can query the
  28.     printer to obtain the correct response.  
  29.     
  30.     My thanks to Mark Fleming for his help with debugging this code.
  31.     
  32.     Change History
  33.     
  34.     1.    Fixed the prevPtr bug where I return the current packetPtr instead of the previous
  35.         packetPtr in the ProcessPSQuery function
  36.     0.  Moved PostScript parsing code to this file.
  37.     
  38. */
  39.  
  40. #include <ctype.h>
  41. #include "StringUtils.h"
  42. #include "PAPServerSample.h"
  43. #include "PAPPostScriptStuff.h"
  44.  
  45. extern PacketPtr    gTempPackPtr;
  46. char                gEOFStr[8] = kEOFStr;
  47. char                gBeginPSStr[8] = "\045\041PS";        // = "%!PS"
  48. char                qBeginQueryStr[8] = kBeginQueryStr;
  49. char                gEndStr[8] = kEndStr;
  50. char                gQueryStr[8] = kQueryStr;
  51. extern OTLIFO*        gFreeQ;    
  52. extern Boolean        gDone;
  53.  
  54.  
  55. // prototypes
  56.  
  57. Boolean FindString(const char *buffer, char *str, SInt16 lenStr, SInt16 *lenMatched,
  58.                         SInt16 *pos, UInt16 numCharsInBuffer, Boolean matchAll);
  59.  
  60.  
  61. /*
  62.     The TestDataIsPSQuery is used to determine whether an incoming packet is the initial packet
  63.     for a postscript query, or is a continuing packet of a prelvious query started with some 
  64.     previous packet.
  65. */
  66. Boolean TestDataIsPSQuery(PacketPtr packetPtr)
  67. {
  68.     MyEndpointRef     *theEp;
  69.     Boolean            result;
  70.  
  71.     theEp = packetPtr->theEp;
  72.  
  73.     if (TstInPSQueryFlag(theEp->flags))    // check if the endpoint is already processing a postscript
  74.         result = true;                    // query for which we have not reached the EndQuery
  75.     else
  76.     {
  77.         result = IsPacketAPSQuery(packetPtr);
  78.         if (result == true)
  79.         {
  80. #if SHOW_DEBUG_FLOW
  81.         
  82.             DebugStr("\p processing a postscript query;g");
  83. #endif
  84.             theEp->prevPtr = nil;    // initialize the prevPtr field to nil
  85.             SetInPSQueryFlag(theEp->flags);
  86.             theEp->psState = kLookingForEndStr;    // indicate that we have identified query beginning
  87.                                         // and that the next step is to look for the EndQuery string
  88.  
  89.                 // set the time Data In timestamp field
  90.             BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataIn, sizeof(OTTimeStamp));
  91.                 // reset the numBytesIn field to zero
  92.             theEp->numBytesIn = 0;
  93.             
  94.         }
  95.     }
  96.     
  97.     return result;
  98. }
  99.  
  100. /*
  101.     ProcessPSQuery as used in this sample looks for the ps end...query string, then
  102.     gets the response and and sends it
  103.     The routine is designed to be called at deferred task time and uses OTAllocMem
  104.     instead of NewPtr or NewHandle
  105.     
  106.     Note that this routine is responsible for enqueueing the processed packet ptr
  107.     to the freeQ, unless it appears that there is a partial match to a string that
  108.     we are looking for at the end of the buffer.  If so, then the packetPtr is
  109.     save with the endpoint ref to be used when the next incoming packet is processed
  110.     
  111.     In this routine, the first while loop is where the contents of the ps query are
  112.     analyzed until the end of the packet is read.  As each default response is read,
  113.     an individual OTSnd call is made to send the default response.  Each response will 
  114.     be made with the T_MORE flag bit set to keep the EOF bit from being sent in the
  115.     PAP responses. This routine does not queue multiple responses to send in a single
  116.     response.
  117.     
  118.     When the end of the packet has been reached, the routine exits the while loop 
  119.     and checks for the EOF indicator signaling the end of the query.  If the 
  120.     EOF is read, then the call is made to SendEmptyPacket, which will result in
  121.     an empty packet with the EOF bit set.  The Apple LaserWriter client requires
  122.     receiving a last packet that has the eof bit set to know that the query has been
  123.     completely processed by the printer/server (even if the printer/server has responded
  124.     to all of the query items).
  125. */
  126. OSStatus ProcessPSQuery(PacketPtr packetPtr)
  127. {
  128.     OSStatus        err;
  129.     MyEndpointRef    *theEp;
  130.     PacketPtr        packPtr;
  131.     UInt16            offset;
  132.     UInt16            matchResult;
  133.     Boolean            done = false;
  134.     Boolean            testflag = false;
  135.  
  136.     theEp = packetPtr->theEp;
  137.         // add the number of bytes in the packet to the numBytesIn field
  138.     theEp->numBytesIn += packetPtr->numBytes;
  139.  
  140.         // set the time Data End timestamp field
  141.     BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataEnd, sizeof(OTTimeStamp));
  142.     
  143.     err = kOTNoError;
  144.         // check whether we need to concatenate the current and previous packet ptrs
  145.     if ((packetPtr == theEp->prevPtr) || (theEp->prevPtr == nil))
  146.     {
  147. #if SHOW_DEBUG_FLOW
  148.         DebugStr("\p no packet to append");
  149. #endif
  150.         testflag = false;
  151.         packPtr = packetPtr;
  152.     }
  153.     else
  154.     {
  155. #if SHOW_DEBUG_FLOW
  156.         DebugStr("\p checking how we append packets");
  157. #endif
  158.         packPtr = gTempPackPtr;
  159.  
  160.             // copy contents of the previous packet buffer to the new buffer
  161.         BlockMove((Ptr)theEp->prevPtr, (Ptr)packPtr, sizeof(PacketBuffer));
  162.         offset = sizeof(PacketBuffer) - kPAPDataSize + (theEp->prevPtr)->numBytes;
  163.         BlockMove((Ptr)&(packetPtr->data), (Ptr)&(packPtr->data[offset]), packetPtr->numBytes);
  164.             // adjust the numbytes field
  165.         packPtr->numBytes += packetPtr->numBytes;
  166.         
  167.             // enqueue the previous packet ptr back to the gFreeQ since I not expecting a 
  168.             // ps default response that overlaps 3 packets.
  169.             // fixed bug here where I release the current packetPtr instead of the previous
  170.             // packetPtr
  171.     
  172.         OTLIFOEnqueue(gFreeQ, &((theEp->prevPtr)->fLink));    // first field is fLink field
  173.     }
  174.     
  175.     while (done == false)
  176.     {
  177.         switch (theEp->psState)
  178.         {
  179.             case kLookingForEndStr:
  180.                 matchResult = FindQueryString(packPtr, kLookingForEndStr);
  181.                 break;
  182.  
  183.             case kLookingForQueryStr:
  184.                 matchResult = FindQueryString(packPtr, kLookingForQueryStr);
  185.                 break;
  186.  
  187.             case kLookingForDefaultResponse:
  188. #if SHOW_DEBUG_FLOW
  189.                 if (testflag)
  190.                     DebugStr("\p about to call ProcessDefaultResponse");
  191. #endif
  192.                 
  193.                 matchResult = ProcessDefaultResponse(packPtr);
  194.                 break;
  195.         }                
  196.                 
  197.         switch (matchResult)
  198.         {
  199.             case kMatch:
  200.                 if (theEp->psState == kLookingForDefaultResponse)
  201.                     theEp->psState = kLookingForEndStr;
  202.                 else
  203.                     theEp->psState += 1;    // increment the state
  204.                 break;
  205.             
  206.             case kPartialMatch:
  207.                 done = true;        // have to come around with the next packet
  208.                 break;
  209.  
  210.             case kNoMatch:
  211. #if SHOW_DEBUG_FLOW
  212.                 DebugStr("\p no match");
  213. #endif
  214.                                     // wasn't able to find a match, not even a 
  215.                                     // partial match, so set the pos to the end of the
  216.                                     // packet
  217.                 packPtr->lastPos = packPtr->numBytes;
  218.                 done = true;        // have to come around with the next packet
  219.                 break;
  220.         }
  221.  
  222.     }
  223.  
  224.         // check to see if we see the EOF flag
  225.         
  226.  
  227.         // this is the last packet of this query.
  228.         // check to see if the lastPos field is set to the end of the buffer
  229.         // this tels us the we are not awaiting a pending match for a partial
  230.         // string
  231.     if (packPtr->lastPos == packPtr->numBytes)
  232.     {
  233.             // reset the lastPos field so that we can search for the EOF string as the
  234.             // last few characters
  235.         packPtr->lastPos -= clen(gEOFStr);
  236.             // search for the EOF string at the end of the packet
  237.         matchResult = FindQueryString(packPtr, kLookingForEOFStr);
  238.             // since we reset lastPos, we need to restore it
  239.         packPtr->lastPos = packPtr->numBytes;
  240.             
  241.         if (matchResult == kMatch)
  242.         {
  243. #if SHOW_DEBUG_FLOW
  244.     
  245.             DebugStr("\p Sending a null packet;g");
  246. #endif
  247.                 // send an empty response with EOF flag set since this is what the Laserwriter 
  248.                 // client wants to see.
  249.             SendEmptyPacket(packPtr);
  250.                 // clear bit that indicates we are processing a postscript query
  251.             ClrInPSQueryFlag(theEp->flags);
  252. #if SHOW_DEBUG_FLOW
  253.     
  254.             DebugStr("\p Have finished ps query;g");
  255. #endif
  256.         }
  257.         
  258.     }
  259.         
  260.     
  261.         // check to see whether we processed all of the bytes in the packet.
  262.     if (packPtr->lastPos == packPtr->numBytes)
  263.     {
  264.         theEp->prevPtr = nil;    // if so, then don't save this packet in the prevPtr field.
  265.  
  266.                 // queue the buffer to the freeQ
  267.         OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));    // first field is fLink field
  268.     }
  269.     else
  270.     {
  271.         theEp->prevPtr = packetPtr;    // we still have bytes to process.
  272.             //adjust the lastPos field
  273.         packetPtr->lastPos = packetPtr->numBytes - (packPtr->numBytes - packPtr->lastPos);
  274.         
  275.     }    
  276.         
  277.     return err;
  278.     
  279. }
  280.  
  281.  
  282. /*
  283.     DoProcessPSQuery is used as the entry point to processing a postscript query.
  284. */
  285. Boolean DoProcessPSQuery(PacketPtr    packetPtr)
  286. {
  287.     OSStatus    err = kOTNoError;
  288.     Boolean        result = false;
  289.         
  290.     if (TestDataIsPSQuery(packetPtr))  // is the packet a PS query
  291.     {
  292.         result = true;        // packet is a PostScript query and is being processed
  293.         err = ProcessPSQuery(packetPtr);
  294.             
  295.         if (err < kOTNoError)
  296.         {
  297.             DoValueBreak(err, "error occured calling ProcessPSQuery #");
  298.                 // show the error and quit the main event loop
  299.             gDone = true;
  300.         }
  301.         
  302.     }
  303.     else
  304.     {
  305.             // we aren't processing PS query packets, so exit this routine
  306.             // the IsPacketQuery routine potentially sets the lastPos field
  307.             // so reset it back to 0;
  308.         packetPtr->lastPos = 0;
  309.     }
  310.     
  311.     return result;
  312. }
  313.  
  314. /*
  315.     FindString is used to find the iterate through the characters passed in the buffer
  316.     to find the string passed in str.  The length of the search string is passed in 
  317.     lenStr.  pos is a pointer to the index where to start searching.  stopChar is the
  318.     last position of the buffer to search
  319.     input 
  320.         buffer - pointer to buffer to search
  321.         str - string to match
  322.         lenStr - length of the str to match in buffer
  323.         pos - index where to begin search
  324.         numCharsInBuffer - index of last valid char in buffer
  325.         matchAll - match both upper and lower cases of the input string.
  326.     
  327.     output -
  328.         lenMatched - number of character which were matched.  If no match, then
  329.                         returns 0.
  330.         pos - index of the next char to look at for the next search.
  331.         
  332.     If the str is not found in the buffer between the pos index and the stopChar,
  333.     *pos returns -1,  If a partial match is made at the end of the buffer, return the
  334.     negative offset
  335. */
  336.  
  337. Boolean FindString(const char *buffer, char *str, SInt16 lenStr, SInt16 *lenMatched,
  338.                         SInt16 *pos, UInt16 numCharsInBuffer, Boolean matchAll)
  339. {
  340.     SInt16        i;
  341.     SInt16        numToCompare;
  342.     Boolean        done = false;
  343.     Boolean        ok;
  344.     
  345.     numToCompare = lenStr;    // set the number of chars to compare as the string len.
  346.     while (done == false)    // search until the flag is set to true.
  347.     {
  348.             // check whether the first character matches the character at the
  349.             // current index position
  350.         if ((buffer[*pos] == str[0]) || ((matchAll == true) && (toupper(buffer[*pos]) == 
  351.                         toupper(str[0]))))
  352.         {
  353.             if (numCharsInBuffer < (*pos + lenStr))
  354.             {
  355.                     // the has too few remaining characters to compare all of the current
  356.                     // string.
  357.                 numToCompare = numCharsInBuffer - *pos;
  358.                 
  359.             }
  360.                 //init OK boolean
  361.             ok = true;
  362.             for (i = 1; (i < numToCompare) && ok; i++)
  363.             {
  364.                 if ((buffer[*pos+i] == str[i]) || ((matchAll == true) && 
  365.                         (toupper(buffer[*pos+1]) == toupper(str[i]))))
  366.                     continue; 
  367.                 else
  368.                     ok = false;
  369.                 
  370.             }
  371.                 // we've done our testing, now see if we found a match
  372.  
  373.             if (ok == true)        // was match found
  374.             {
  375.                 *lenMatched = numToCompare;        // return the number of chars matched
  376.                 *pos += numToCompare;            // return the next position in the buffer to start next search
  377.                 done = true;                    // search finished.
  378.             }
  379.         }
  380.         
  381.         if (done == false)
  382.         {
  383.             *pos += 1;        // no match found so increment the place to start the next search iteration
  384.             if ((*pos+1) == numCharsInBuffer)
  385.             {
  386.                     // no more characters in the buffer to search
  387.                 *lenMatched = 0;        // set len matched to nothing    
  388.                 done = true;            // search finished.
  389.             }
  390.         }
  391.         
  392.     }
  393.     
  394.     return (*lenMatched != 0);
  395. }
  396.  
  397. /*
  398.     IsPacketAPSQuery is used to check whether the contents of a packet are the beginning of
  399.     a PostScript query.  This can be found since there will be the signature stuff
  400.     at the beginning of the query.
  401. */
  402. Boolean IsPacketAPSQuery(PacketPtr packetPtr)
  403. {
  404.     SInt16        lenMatched, pos;
  405.     Boolean     result;
  406.     
  407.     pos = 0;        // start search from the beginning of the buffer
  408.     result = FindString((char*)&(packetPtr->data), gBeginPSStr, clen(gBeginPSStr),
  409.                 &lenMatched, &pos, 4, kCaseMatchAll);    // must find the "%!PS" string in position 3 & 4 of the buffer
  410.     if (result == true)
  411.     {
  412.             // the search found something in the first 4 characters of the buffer
  413.         if (lenMatched < clen(gBeginPSStr))
  414.         {
  415.             result = false;        // didn't match 2 characters
  416.         }
  417.         else
  418.         {
  419.             result = FindString((char*)&(packetPtr->data), qBeginQueryStr, 
  420.                             clen(qBeginQueryStr), &lenMatched, 
  421.                             &pos, 24, kCaseMatchAll);        // must find "Query\015" in pos 15 - 20 of the buffer
  422.             if (result == true)
  423.             {
  424.                 if (lenMatched < clen(qBeginQueryStr))
  425.                     result = false;
  426.                 else
  427.                 {
  428.                         // we're going to return a true result, to set the packetPtr.lastPos
  429.                         // field to the current pos field.  When we start our next search, we 
  430.                         // do so at this position.
  431.                     packetPtr->lastPos = pos;
  432.                 }
  433.             }
  434.         }
  435.     }
  436.     
  437.     return result;
  438. }
  439.  
  440. UInt16    FindQueryString(PacketPtr packetPtr, SInt16 whichStr)
  441. {
  442.     SInt16        lenMatched, pos;
  443.     UInt16        result1, len;
  444.     Boolean        result;
  445.     char        *str;
  446.     
  447.     result1 = kNoMatch;                // initialize to no Match
  448.     pos = packetPtr->lastPos;        // start search from where we last stopped looking in buffer
  449.                                     // first find the query prefix
  450.     
  451.     switch (whichStr)
  452.     {
  453.         case kLookingForEndStr:
  454.             str = gEndStr;
  455.             len = clen(gEndStr);
  456.             break;
  457.         
  458.         case kLookingForQueryStr:
  459.             str = gQueryStr;
  460.             len = clen(gQueryStr);
  461.             break;
  462.             
  463.         case kLookingForEOFStr:
  464.             str = gEOFStr;
  465.             len = clen(gEOFStr);
  466.             break;
  467.     }
  468.  
  469.     result = FindString((char*)&(packetPtr->data), str, len,
  470.                 &lenMatched, &pos, packetPtr->numBytes, kCaseMustMatch);    
  471.     
  472.     if (result == true)
  473.     {
  474.             // check that the proper string len was returned
  475.         if (lenMatched < len)
  476.         {
  477.                 // we have reached the end of the buffer and have found a partial match.
  478.             result1 = kPartialMatch;
  479.                 // set the lastPos field to where the partial match begins
  480.             packetPtr->lastPos = pos - lenMatched;
  481.         }
  482.         else
  483.         {
  484.                 // all parts found
  485.             result1 = kMatch;
  486.                 // complete match found, so advance the lastPos field
  487.             packetPtr->lastPos = pos;
  488.         }
  489.     }
  490.     
  491.     return result1;
  492.     
  493. }
  494.  
  495.  
  496. UInt16 ProcessDefaultResponse(PacketPtr packetPtr)
  497. {
  498.     OTResult    otErr;
  499.     UInt32        pos, begin;
  500.     UInt16        result;
  501.     char        response[255];
  502.  
  503.         // since we've found the end of the query chars, then there is at least a 
  504.         // partial match
  505.     result = kPartialMatch;
  506.         // set pos to the lastPos position since this is the beginning of 
  507.         // the query
  508.     begin = pos = packetPtr->lastPos;
  509.     
  510.         // remove the leading space character which may precede the default response
  511.     while (packetPtr->data[pos] == kSpaceChar)
  512.     {
  513.         pos++;
  514.         begin++;
  515.     }    
  516.     
  517.         // gather the default response characters    
  518.     while ((packetPtr->data[pos] != kReturnChar) && (pos < packetPtr->numBytes))
  519.     {
  520.             // gather default response characters
  521.         response[pos - begin] = packetPtr->data[pos];
  522.             // increment the counter
  523.         pos++;
  524.     }
  525.     
  526.         // go ahead and set the terminating null character for the "c" string
  527.     if ((pos - begin) < 255)
  528.         response[pos - begin] = 0;
  529.     else
  530.     {
  531.             // this should never happen, but if it does, I want to know about it.
  532. #if SHOW_DEBUG_FLOW
  533. //        DebugStr((const unsigned char *)"\p response greater than 255 chars");
  534. #endif
  535.         response[255] = 0;
  536.     }
  537.             
  538.     if (packetPtr->data[pos] == kReturnChar)
  539.     {
  540.             // we have found the end of the default response 
  541.             // catenate a line feed character to the  response 
  542.     
  543.         ccatchr(response, kLineFeedChar, 1);
  544.  
  545.         otErr = OTSnd((packetPtr->theEp)->ep, response, clen(response), T_MORE);
  546.         if (otErr < 0)
  547.         {
  548. #if SHOW_DEBUG_FLOW
  549.             DoValueBreak((long)otErr, "error occured sending PS response #");
  550. #endif
  551.         }
  552.         packetPtr->lastPos = pos;
  553.         result = kMatch;
  554.     }
  555.  
  556. #if SHOW_DEBUG_FLOW
  557.     ccatchr(response, ';', 1);
  558.     ccatchr(response, 'g', 1);
  559.     c2p(response);
  560. //    DebugStr((const unsigned char*)response);
  561. #endif
  562.  
  563.     
  564.     return result;
  565. }
  566.  
  567. /*
  568.     SendEmptyPacket is implemented to send an empty packet with the T_MORE flag not set.
  569.     The Apple LaserWriter client requires that the last response to a Postscript query
  570.     have the EOF flag set.  By having the EOM option enabled and sending a packet with 
  571.     the T_MORE flag not set, OT/PAP will send an empty packet that has the EOF bit set.
  572. */
  573. void SendEmptyPacket(PacketPtr packetPtr)
  574. {
  575.     OTResult    err;
  576.     char        response[16];
  577.  
  578.     err = OTSnd((packetPtr->theEp)->ep, &response, 0, 0);
  579.     if (err != kOTNoError)
  580.     {
  581. #if SHOW_DEBUG_FLOW
  582.         DoValueBreak((long)err, "error occured sending empty packet #");
  583. #endif
  584.     }
  585.     
  586. }